import 'dart:typed_data';
import 'package:bluetooth_protocols_plugin/src/base/data_base.dart';
import 'package:bluetooth_protocols_plugin/src/tools/bit_utils.dart';
import 'package:bluetooth_protocols_plugin/src/tools/utils.dart';
import 'package:bluetooth_protocols_plugin/src/lorin/message/lorin_request_body.dart';
import 'package:bluetooth_protocols_plugin/src/base/enum.dart';

// Lorin帧包中的数据段
class LorinData extends DataBase {
  // 数据类型（标准组/专用组）
  LorinDataFormatType dataFormatType = LorinDataFormatType.none;

  // 源设备App
  int? sourceDeviceNum;

  // 目标设备
  int? targetDeviceNum;

  // 通道(标准组)
  LorinDataChannelType? channelType;

  // 属性(标准组)
  LorinDataPropertyType? propertyType;

  // cmd
  Uint8List? cmdList;

  // cmd
  int? cmdIntValue;

  // 参数
  Uint8List? para;

  // 是否是请求数据
  bool isRsp = false;

  // 是否是写数据
  bool isWrite = false;

  LorinData.from(LorinRequestBody requestBody) {
    dataFormatType = requestBody.dataFormat!;
    sourceDeviceNum = requestBody.sourceDevice;
    targetDeviceNum = requestBody.targetDevice;
    channelType = requestBody.channel;
    propertyType = requestBody.property;
    cmdList = requestBody.cmdList;
    isRsp = false;
    // 不是所有带着参数param的指令都是写指令
    // 比如获取单次统计，携带参数起始编号和数目
    // 但它不是写指令
    // 所以这里需要额外传递一个isWrite参数
    // 优先级高于param，就算param不为空，但isWrite是false
    isWrite = requestBody.isWrite == null
        ? (requestBody.param != null)
        : requestBody.isWrite!;
    para = requestBody.param;
  }

  LorinData.fromList(LorinDataFormatType dataFormat, Uint8List data) {
    // 获取通道和属性
    getChannelAndProperty(int channelValue, int propertyValue) {
      isRsp = BitUtils.getBit(channelValue, 7) == 1;
      channelType = LorinDataChannelType.getTypeByNum(
          BitUtils.getLowBits(channelValue, 7));
      isWrite = BitUtils.getBit(propertyValue, 7) == 1;
      propertyType = LorinDataPropertyType.getTypeByNum(
          BitUtils.getLowBits(propertyValue, 7));
    }

    // 获取cmd
    Uint8List getCmd(Uint8List data) {
      var val = Utils.decodeCmd(data);
      // 拿到cmd的int值
      cmdIntValue = val.$1;
      int cmdLength = val.$2;
      return data.sublist(0, cmdLength);
    }

    dataFormatType = dataFormat;
    switch (dataFormat) {
      case LorinDataFormatType.norCpp:
        getChannelAndProperty(data[0], data[1]);
        para = data.sublist(2);
        break;
      case LorinDataFormatType.norTcpp:
        targetDeviceNum = data[0];
        getChannelAndProperty(data[1], data[2]);
        para = data.sublist(3);
        break;
      case LorinDataFormatType.norStcpp:
        sourceDeviceNum = data[0];
        targetDeviceNum = data[1];
        getChannelAndProperty(data[2], data[3]);
        para = data.sublist(4);
        break;
      case LorinDataFormatType.speCp:
        cmdList = getCmd(data.sublist(0));
        int dataStartIndex = 0 + cmdList!.length;
        para = data.length > dataStartIndex
            ? data.sublist(cmdList!.length)
            : Uint8List(0);
        break;
      case LorinDataFormatType.speTcp:
        targetDeviceNum = data[0];
        cmdList = getCmd(data.sublist(1));
        int dataStartIndex = 1 + cmdList!.length;
        para = data.length > dataStartIndex
            ? data.sublist(1 + cmdList!.length)
            : Uint8List(0);
        break;
      case LorinDataFormatType.speStcp:
        sourceDeviceNum = data[0];
        targetDeviceNum = data[1];
        cmdList = getCmd(data.sublist(2));
        int dataStartIndex = 2 + cmdList!.length;
        para = data.length > dataStartIndex
            ? data.sublist(dataStartIndex)
            : Uint8List(0);
        break;
      default:
        break;
    }
  }

  @override
  int dataIntValue() {
    var s = sourceDeviceNum ?? 0;
    var t = targetDeviceNum ?? 0;
    var c = BitUtils.getNewBit(channelType?.rawValue ?? 0, 8, isRsp);
    var p = BitUtils.getNewBit(propertyType?.rawValue ?? 0, 8, isWrite);
    var cmd_ = Utils.computeDataIntVal(cmdList ?? Uint8List.fromList([0]));
    var para_ = para == null ? 0 : Utils.computeDataIntVal(para!);
    switch (dataFormatType) {
      case LorinDataFormatType.norCpp:
        return c + p + para_;
      case LorinDataFormatType.norTcpp:
        return t + c + p + para_;
      case LorinDataFormatType.norStcpp:
        return s + t + c + p + para_;
      case LorinDataFormatType.speCp:
        return cmd_ + p;
      case LorinDataFormatType.speTcp:
        return t + cmd_ + p;

      case LorinDataFormatType.speStcp:
        return s + t + cmd_ + p;
      default:
        return 0;
    }
  }

  @override
  int dataLength() {
    var paraLength = para?.length ?? 0;
    int cmdLength = cmdList?.length ?? 0;
    switch (dataFormatType) {
      case LorinDataFormatType.norCpp:
        return paraLength + 2;
      case LorinDataFormatType.norTcpp:
        return paraLength + 3;
      case LorinDataFormatType.norStcpp:
        return paraLength + 4;
      case LorinDataFormatType.speCp:
        return paraLength + cmdLength;
      case LorinDataFormatType.speTcp:
        return paraLength + cmdLength + 1;
      case LorinDataFormatType.speStcp:
        return paraLength + cmdLength + 2;
      default:
        return 0;
    }
  }

  @override
  Uint8List toBytes() {
    List<int> dataList = [];
    var s = sourceDeviceNum ?? 0;
    var t = targetDeviceNum ?? 0;
    var c = BitUtils.getNewBit(channelType?.rawValue ?? 0, 7, isRsp);
    var p = BitUtils.getNewBit(propertyType?.rawValue ?? 0, 7, isWrite);
    var cmd_ = cmdList ?? Uint8List.fromList([0]);
    switch (dataFormatType) {
      case LorinDataFormatType.norCpp:
        dataList.addAll([c, p]);
        break;
      case LorinDataFormatType.norTcpp:
        dataList.addAll([t, c, p]);
        break;
      case LorinDataFormatType.norStcpp:
        dataList.addAll([s, t, c, p]);
        break;
      case LorinDataFormatType.speCp:
        dataList.addAll(cmd_);
        break;
      case LorinDataFormatType.speTcp:
        dataList.add(t);
        dataList.addAll(cmd_);
        break;
      case LorinDataFormatType.speStcp:
        dataList.addAll([s, t]);
        dataList.addAll(cmd_);
        break;
      default:
        break;
    }
    // 添加参数
    if (para != null) dataList.addAll(para!);
    return Uint8List.fromList(dataList);
  }

  @override
  Map<String, dynamic> toMap() {
    return {
      'dataFormatType': dataFormatType,
      'sourceDeviceNum': sourceDeviceNum,
      'targetDeviceNum': targetDeviceNum,
      'channelType': channelType,
      'propertyType': propertyType,
      'cmdList': cmdList,
      'cmdIntValue': cmdIntValue,
      'para': para,
      'isRsp': isRsp,
      'isWrite': isWrite,
    };
  }
}
